
#include <unistd.h>
#include <stdio.h>
#include <time.h>

#include "../../mem/mem.h"
#include "../../mem/shm_mem.h"
#include "../../dprint.h"
#include "../../hashes.h"

#include "imc_mng.h"

extern imc_hentry_p _imc_htable;
extern int imc_hash_size;

#define imc_get_hentry(_hid, _size) ((_hid)&(_size-1))

/**
 * hash thable init
 */
int imc_htable_init(void)
{/*{{{*/
    int i = 0;
    
    if(0 >= imc_hash_size)
    {
        LM_ERR("invalid hash table size\n");
        return -1;
    }

    _imc_htable = (imc_hentry_p)shm_malloc(imc_hash_size*sizeof(imc_hentry_t));
    if(NULL == _imc_htable)
    {
        LM_ERR("no more shm memory for hash table\n");
        return -1;
    }

    memset(_imc_htable, 0, imc_hash_size*sizeof(imc_hentry_t));
    for(i = 0; i < imc_hash_size; i++)
    {
        if(0 == lock_init(&_imc_htable[i].lock))
        {
            LM_CRIT("failed to initialize lock [%d] of the hash tables",i);
            goto error;
        }
    }
    
    return 0;

error:
    if(NULL != _imc_htable)
    {
        shm_free(_imc_htable);
        _imc_htable = NULL;
    }
  
    return -1;
}/*}}}*/

/** 
 * destroy hash table
 */
int imc_htable_destroy(void)
{/*{{{*/
    int i = 0;
    imc_room_p irp = NULL;
    imc_room_p irp_temp = NULL;

    if(NULL == _imc_htable)
    {
        return -1;
    }
    
    for(i = 0; i < imc_hash_size; i++)
    {
        lock_destroy(&_imc_htable[i].lock);
        if(NULL == _imc_htable[i].rooms)
            continue;

        irp = _imc_htable[i].rooms;
        while(irp)
        {
            irp_temp = irp->next;
            imc_del_room(&irp->name, &irp->domain);
            irp = irp_temp;
        }
    }

    shm_free(_imc_htable);
    _imc_htable = NULL;

    return 0;
}/*}}}*/



/** 
 * delete unactive room
 */
int imc_del_unactive_room(long time_clean)
{/*{{{*/
    int i = 0;
    time_t timep = 0;
    imc_room_p irp = NULL;
    imc_room_p irp_temp = NULL;
    imc_member_p imp = NULL, imp_temp = NULL;

    if(NULL == _imc_htable)
    {
        return -1;
    }

    time(&timep);
    for(i = 0; i < imc_hash_size; i++)
    {
        if(NULL == _imc_htable[i].rooms)
        {
            continue;
        }

        LM_DBG("imc_htable %d is not nulll",i);
        lock_get(&_imc_htable[i].lock);
        irp = _imc_htable[i].rooms;
        while(irp)
        {
            irp_temp = irp->next;
            LM_DBG("room = %.*s,timep = %ld, irp->time = %ld, time_clean = %ld",
                   irp->name.len, irp->name.s, timep, irp->time, time_clean);
            if((timep - irp->time) > time_clean)
	    {
		if(irp->prev == NULL)
	            _imc_htable[i].rooms = irp->next;
		else
	            irp->prev->next = irp->next;
		if(irp->next != NULL)
		    irp->next->prev = irp->prev;

		/* delete members */
                imp = irp->members;
		while(imp){
		    imp_temp = imp->next;
	            shm_free(imp);
	            imp = imp_temp;
		}
                LM_DBG("delete room %.*s\n",irp->name.len, irp->name.s);
		shm_free(irp);
	    }
	    irp = irp_temp;
        }
	lock_release(&_imc_htable[i].lock);
    }

    return 0;
}/*}}}*/


/**
 * add room
 */
imc_room_p imc_add_room(str* name, str* domain)
{/*{{{*/
    imc_room_p irp = NULL;
    int size = 0;
    int hidx = 0;

    if(NULL == name || NULL == name->s || 0 >= name->len || NULL == domain || NULL == domain->s || 0 >= domain->len)
    {
        LM_ERR("invalid parameters\n");
        return NULL;
    }

    /* struct size + "sip:" + name len + "@" + domain len + '\0' */
    size = sizeof(imc_room_t) + (name->len + domain->len + 6)*sizeof(char);
    irp = (imc_room_p)shm_malloc(size);
    if(NULL == irp)
    {
        LM_ERR("no more shm memory left for the room\n");
        return NULL;
    }
    memset(irp, 0, size);

    /* sip:name@domain*/
    irp->uri.len = 4 /*sip:*/ + name->len + 1 /*@*/ +domain->len;
    irp->uri.s = (char *)(((char *)irp) + sizeof(imc_room_t));

    memcpy(irp->uri.s, "sip:", 4);
    memcpy(irp->uri.s + 4, name->s, name->len);
    irp->uri.s[4 + name->len] = '@';
    memcpy(irp->uri.s + 5 + name->len, domain->s, domain->len);
    irp->uri.s[irp->uri.len] = '\0';

    irp->name.len = name->len;
    irp->name.s   = irp->uri.s + 4;
    irp->domain.len = domain->len;
    irp->domain.s   = irp->uri.s + 5 + name->len;

    time(&(irp->time));

    irp->hashid = core_case_hash(&irp->name, &irp->domain, 0);
    hidx = imc_get_hentry(irp->hashid, imc_hash_size);

    lock_get(&_imc_htable[hidx].lock);

    if( NULL != _imc_htable[hidx].rooms)
    {
        irp->next = _imc_htable[hidx].rooms;
        _imc_htable[hidx].rooms->prev = irp;
        _imc_htable[hidx].rooms = irp;
    }
    else
    {
        _imc_htable[hidx].rooms = irp;
    }

    return irp;
}/*}}}*/


/**
 * release room
 */
int imc_release_room(imc_room_p room)
{
	unsigned int hidx;
	
	if(NULL == room)
	{
		LM_ERR("invalid parameters\n");
		return -1;
	}
	
	hidx = imc_get_hentry(room->hashid, imc_hash_size);
	lock_release(&_imc_htable[hidx].lock);

	return 0;
}

/**
 * search room
 */
imc_room_p imc_get_room(str* name, str* domain)
{/*{{{*/
    imc_room_p irp = NULL;
    unsigned int hashid;
    int hidx;

    if(NULL == name || NULL == name->s || 0 >= name->len || NULL == domain || NULL == domain->s || 0 >= domain->len)
    {
        LM_ERR("invalid parameters\n");
        return NULL;
    }

    hashid = core_case_hash(name, domain, 0);
    hidx = imc_get_hentry(hashid, imc_hash_size);

    lock_get(&_imc_htable[hidx].lock);
    irp = _imc_htable[hidx].rooms;

    while(irp)
    {
        if(irp->hashid == hashid && irp->name.len == name->len 
            && irp->domain.len == domain->len
            && !strncasecmp(irp->name.s, name->s, name->len)
            && !strncasecmp(irp->domain.s, domain->s, domain->len))
        {
            time(&(irp->time));
            return irp;
        }
        irp = irp->next;
    }

    /* no room */
    lock_release(&_imc_htable[hidx].lock);
    return NULL;
}/*}}}*/

/* delete room */

int imc_del_room(str *name, str * domain)
{
    imc_room_p irp = NULL;
    imc_member_p imp = NULL, imp_temp = NULL;
    unsigned int hashid;
    int hidx;

    if(name == NULL || name->s == NULL || name->len <=0 
        || domain == NULL || domain->s == NULL || domain->len <= 0)
    {
        LM_ERR("invalid parameters\n");
        return -1;
    }
    hashid = core_case_hash(name, domain, 0);

    hidx = imc_get_hentry(hashid, imc_hash_size);

    lock_get(&_imc_htable[hidx].lock);
    irp = _imc_htable[hidx].rooms;
    while(irp)
    {
        if(irp->hashid == hashid && irp->name.len==name->len
            && irp->domain.len == domain->len
            && !strncasecmp(irp->name.s, name->s, name->len)
            && !strncasecmp(irp->domain.s, domain->s, domain->len))
        {
            if(irp->prev == NULL)
                _imc_htable[hidx].rooms = irp->next;
            else
                irp->prev->next = irp->next;
            if(irp->next != NULL)
                irp->next->prev = irp->prev;
            
            /* delete members */
            imp = irp->members;
            while(imp){
                imp_temp = imp->next;
                shm_free(imp);
                imp = imp_temp;
            }
            shm_free(irp);
            goto done;
        }
        irp = irp->next;
    }
done:
    lock_release(&_imc_htable[hidx].lock);
    return 0;
}


/**
*   add member
*/
imc_member_p imc_add_member(imc_room_p room, str *user, str *domain)
{
    imc_member_p imp = NULL;
    int size =  -1;

    if(room == NULL || user == NULL || user->s == NULL || user->len <= 0
        || domain == NULL || domain->s == NULL || domain->len <= 0)
    {
        LM_ERR("invalid parameters\n");
        return NULL;
    }

    /* struct size + "sip:" + user name len + "@" + domain len + '\0' */
    size = sizeof(imc_member_t) + (user->len + domain->len + 6)*sizeof(char);
    imp = (imc_member_p)shm_malloc(size);
    if(imp == NULL)
    {
        LM_ERR("out of shm memory\n");
        return NULL;
    }
    memset(imp, 0, size);

    imp->uri.len = 4 /*sip:*/ + user->len + 1 /*@*/ + domain->len;
    imp->uri.s = (char*)(((char*)imp) + sizeof(imc_member_t));
    memcpy(imp->uri.s, "sip:", 4);
    memcpy(imp->uri.s+4, user->s, user->len);
    imp->uri.s[4+user->len] = '@';
    memcpy(imp->uri.s+5+user->len, domain->s, domain->len);
    imp->uri.s[imp->uri.len] = '\0';

    LM_DBG("[uri]= %.*s\n", imp->uri.len, imp->uri.s);
    imp->user.len = user->len;
    imp->user.s = imp->uri.s + 4;

    LM_DBG("[user]= %.*s\n", imp->user.len, imp->user.s);
    imp->domain.len = domain->len;
    imp->domain.s = imp->uri.s + 5 + user->len;

    imp->flags = 0;
    imp->hashid = core_case_hash(&imp->user, &imp->domain, 0);

    room->nr_of_members++;

    if(room->members == NULL)
        room->members = imp;
    else{
        imp->next = room->members->next;
        if((room->members)->next != NULL)
            ((room->members)->next)->prev = imp;
        imp->prev = room->members;
        room->members->next = imp;
    }
    return imp;
}

/**
*   search member
*/

imc_member_p imc_get_member(imc_room_p room, str* user, str* domain)
{/*{{{*/
    imc_member_p imp = NULL;
    unsigned int hashid;

    if(room==NULL || user == NULL || user->s == NULL || user->len <= 0
        || domain == NULL || domain->s == NULL || domain->len <= 0)
    {
        LM_ERR("invalid parameters\n");
        return NULL;
    }

    hashid = core_case_hash(user, domain, 0);
    imp = room->members;
    while(imp)
    {
        if(imp->hashid==hashid && imp->user.len==user->len
            && imp->domain.len==domain->len
            && !strncasecmp(imp->user.s, user->s, user->len)
            && !strncasecmp(imp->domain.s, domain->s, domain->len))
        {
            LM_DBG("found members\n");
            return imp;
        }
        imp = imp->next;
    }
    return NULL;
}/*}}}*/

/**
*   delete members
*/
int imc_del_member(imc_room_p room, str* user, str* domain)
{
    imc_member_p imp = NULL;
    unsigned int hashid;

    if(room==NULL || user == NULL || user->s == NULL || user->len <= 0
        || domain == NULL || domain->s == NULL || domain->len <= 0)
    {
        LM_ERR("invalid parameters\n");
        return -1;
    }

    hashid = core_case_hash(user, domain, 0);
    imp = room->members;
    while(imp)
    {
        if(imp->hashid==hashid && imp->user.len == user->len
            && imp->domain.len == domain->len
            && !strncasecmp(imp->user.s, user->s, user->len)
            && !strncasecmp(imp->domain.s, domain->s, domain->len))
        {
            if(imp->prev==NULL)
                room->members = imp->next;
            else
                imp->prev->next = imp->next;
            if(imp->next != NULL)
                imp->next->prev = imp->prev;
            shm_free(imp);
            room->nr_of_members--;
            return 0;
        }
        imp = imp->next;
    }
    return 0;
}
